// ==UserScript==
// @name         JAVDB_影片预告
// @namespace    http://tampermonkey.net/
// @version      1.0.073
// @description  JAVDB自动显示预告片
// @author       A9
// @supportURL   https://sleazyfork.org/zh-CN/scripts/450740/feedback
// @source       https://github.com/bigwolf9987/JavBusTrailer
// @match        https://www.javbus.com/
// @include      /^https?:\/\/(?:[A-Za-z0-9]+\.)*(?:javdb[0-9]{3})(?:\.[A-Za-z0-9]+)?\/*/
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_setClipboard
// @connect      r18.com
// @connect      dmm.co.jp
// @connect      javdb.com
// @connect      mgstage.com
// @connect      prestige-av.com
// @connect      javspyl.tk
// @connect      heyzo.com
// @connect      avfantasy.com
// @connect      tokyo-hot.com
// @connect      caribbeancom.com
// @connect      aventertainments.com
// @connect      10musume.com
// @connect      pacopacomama.com
// @connect      1pondo.tv
// @connect      cloudfront.net
// @icon         
// @require      https://fastly.jsdelivr.net/npm/video.js@7.10.2/dist/video.min.js
// @require      https://fastly.jsdelivr.net/npm/videojs-vr@1.10.1/dist/videojs-vr.min.js
// @require      https://fastly.jsdelivr.net/npm/dompurify@2.4.0/dist/purify.min.js
// @resource     video-js-css https://fastly.jsdelivr.net/npm/video.js@7.10.2/dist/video-js.min.css
// @resource     video-vr-js-css https://fastly.jsdelivr.net/npm/videojs-vr@1.10.1/dist/videojs-vr.css
// @license      MIT
// ==/UserScript==

(function () {
    "use strict";
    let player = null;
    //脚本设置项
    const settings = {
        enable_vr_mode: 1, //是否使用VR模式，0 关闭；1 开启
        enable_mute_play: 0, //是否开启静音播放，0 关闭；1 开启 （注：跨域页面无效，需手动控制播放与音量）
        video_playback_speed: 1.0, //视频默认播放速度，建议设置范围 0.25～2.0（注：数值越大播放速度越快）
        enable_debug_mode: 0,
        video_quality: 720, //视频清晰度,可设置为下列值之一：1080；720；480；360；240；（注：数值越大越清晰，所需网络加载时间越长）
    };
    // let postfixIndex = 0;
    // const postfixConnfig = {
    //     xxx: [{num: 2, prefix: "hhb"}, {num: 1, prefix: "mhb"}, {num: 0, prefix: "_dmb_w"}],
    //     miab: [{num: 0, prefix: "hhb"}],
    //     npjs: [{num: 0, prefix: "hhb"}],
    //     start: [{num: 0, prefix: "hhb"}],
    //     pred: [{num: 515, prefix: "mhb"}],
    //     dass: [{num: 200, prefix: "mhb"}, {num: 0, prefix: "_dmb_w"}],
    //     aege: [{num: 0, prenum: "", precorp: "1", prefix: "mhb"}],
    //     vec: [{num: 390, prenum: "00"}, {num: 0, prenum: ""}],
    //     shkd: [{num: 470, prefix: "_dmb_w"}, {num: 363, prefix: "_dmb_s"}, {num: 0, prenum: "", prefix: "_dmb_s"}],
    //     same: [{num: 0, prefix: "hhb"}],
    //     adn: [{num: 0, prefix: "hhb"}],
    //     rbk: [{num: 0, prefix: "hhb"}],
    //     atid: [{num: 0, prefix: "hhb"}],
    //     jbd: [{num: 0, prefix: "hhb"}],
    //     sspd: [{num: 0, prefix: "hhb"}],
    //     huntc: [{num: 0, prefix: "hhb"}],
    //     huntb: [{num: 0, prefix: "mhb"}],
    // };
    const postfixConnfig = {
        xxx: [{prefix: "hhb"}, {prefix: "mhb"}, {prefix: "_dmb_w"}],
        shkd: [{prefix: "_dmb_w"}, {prefix: "_dmb_s"}, {prenum: "", prefix: "_dmb_s"}],
        same: [{prefix: "hhb"}],
        adn: [{prefix: "hhb"}],
        rbk: [{prefix: "hhb"}],
        atid: [{prefix: "hhb"}],
        jbd: [{prefix: "hhb"}],
        sspd: [{prefix: "hhb"}],
        huntc: [{prefix: "hhb"}],
        huntb: [{prefix: "mhb"}],
        soan: [{prefix: "_dmb_w"}, {prenum: "", prefix: "mhb"}, {prenum: "00", prefix: "_mhb_w"}],
        venu: [{prenum: "", prefix: "_dmb_w"}, {prenum: "00", prefix: "_dmb_w"}, {prenum: "", prefix: "_sm_w"}],
        fset: [{prenum: "", prefix: "_dmb_w"}, {prenum: "00", prefix: "_dmb_w"}],
        aarm: [{prenum: "00", prefix: "mhb"}, {prenum: "", prefix: "_mhb_w"}],
        avop: [{prefix: "_dmb_w"}, {prenum: "", precorp: "1", prefix: "_dmb_w"}],
        dvaj: [{prenum: "00", prefix: "hhb"}, {prenum: "00", prefix: "mhb"}, {prenum: "", prefix: "hhb"}],
        ntr: [{prenum: "00", prefix: "_dmb_w"}, {prenum: "", prefix: "_dmb_w"}],
        vgd: [{prefix: "_dmb_w"}, {prefix: "_dmb_s"}],
        star: [{prefix: "_dmb_w"}, {prefix: "_sm_w"}, {prefix: "_dmb_s"}],
    };
    const corporations = {
        stars: ["1"],
        star: ["1"],
        svdvd: ["1"],
        sdde: ["1"],
        mogi: ["1"],
        dvdes: ["1"],
        fset: ["1"],
        rct: ["1"],
        wawa: ["1"],
        hawa: ["1"],
        sdnm: ["1"],
        dldss: ["1"],
        drpt: ["1"],
        dandy: ["1"],
        sw: ["1"],
        sdab: ["1"],
        sdjs: ["1"],
        sdmf: ["1"],
        sdmm: ["1"],
        sdmua: ["1"],
        piyo: ["1"],
        rctd: ["1"],
        fsdss: ["1"],
        sdmu: ["1"],
        sun: ["1"],
        kire: ["1"],
        aege: ["1"],
        wo: ["1"],
        sdth: ["1"],
        nhdtb: ["1"],
        nhdta: ["1"],
        hbad: ["1"],
        ftht: ["1", "00"],
        shh: ["1", "00"],
        shn: ["1", "00"],
        mtall: ["1", "00"],
        akdl: ["1", "00"],
        silkc: ["1", "00"],
        ntr: ["1", "00"],
        //
        abp: ["118"],
        abs: ["118"],
        ppt: ["118"],
        chn: ["118"],
        abw: ["118"],
        docp: ["118"],
        ftn: ["118"],
        //
        hgot: ["84"],
        mds: ["84"],
        scpx: ["84"],
        xrw: ["84"],
        gvg: ["13"],
        ovg: ["13"],
        jksr: ["57"],
        mcsr: ["57"],
        dv: ["53"],
        wkd: ["2", "00"],
        t28: ["55"],
        lol: ["12", "00"],
        dtvr: ["24", "00"],
        umd: ["125"],
        //
        awd: [""],
        apns: [""],
        dvaj: [""],
        vema: [""],
        venx: [""],
        hoks: [""],
        sqis: [""],
        real: [""],
        urkk: [""],
        bazx: [""],
        mdbk: [""],
        mdtm: [""],
        mkmp: [""],
        saba: [""],
        scop: [""],
        apaa: [""],
        ekdv: [""],
        aran: [""],
        aed: [""],
        anb: [""],
        cmv: [""],
        sinn: [""],
        hkd: [""],
        rvg: [""],
        scd: [""],
        gvh: [""],
        vec: [""],
        gma: [""],
        bobb: [""],
        focs: [""],
        tppn: [""],
        pkpd: [""],
        //aarm: [""],
        sqte: [""],
        flav: [""],
        jsop: [""],
        bda: [""],
        sora: [""],
        soan: [""],
        nsfs: ["", "00"],
        adn: ["", "00"],
        //
        hzgb: ["h_1100"],
        haru: ["h_687"],
        hz: ["h_113"],
        pym: ["h_283"],
        fone: ["h_491"],
        spz: ["h_254"],
        udak: ["h_254"],
        jukf: ["h_227"],
        rebd: ["h_346"],
        zex: ["h_720"],
        ofku: ["h_254"],
        supa: ["h_244"],
        tki: ["h_286"],
        nacr: ["h_237"],
        tmvi: ["h_452"],
        vgd: ["h_172"],
        mxgs: ["h_068"],
        //
        spro: ["h_1594", "00"],
        stcv: ["h_1616", "00"],
        skmj: ["h_1324"],
        fsvr: ["h_955", "00"],
        pydvr: ["h_1321", "00"],
        shind: ["h_1560", "00"],
        meko: ["h_1160", "00"],
        fgan: ["h_1440", "00"],
        fera: ["h_086", "00"],
        fuga: ["h_086", "00"],
        ktra: ["h_094", "00"],
        jrze: ["h_086", "00"],
        mesu: ["h_086", "00"],

    };
    const need_cors_domain = new Set(["smovie.1pondo.tv", "dy43ylo5q3vt8.cloudfront.net"]);
    DOMPurify.setConfig({
        KEEP_CONTENT: false,
        FORBID_ATTR: ["style"],
        FORBID_TAGS: ["img", "svg", "style"],
        RETURN_DOM: true,
    });

	$(document).ready(function(){
		//Start running from here
		let movieInfo = Common.getMovieInfo();
		if (movieInfo?.movieId) {
			console.log("预告片番号信息 -----------------------" + movieInfo.movieId + "   有码=" + movieInfo.isEuropeOrAmerica);
			BottomPanel.addButtonBottomList();

			Cill.addCiliButton();
			PreVideoDetail.addMoivePreVideo();
		}
		BigReimage.addReimageButton();

		// 顶部按钮组
		TopPanel.addTopButtonList();

		// 列表增加预览按钮
		// addPreplayButtonList();
	});
    


    // ******************************************************************************************************************************************************
    //#region Other

    /**
     * Append Video Player Element to Page
     * @param {object} movieInfo movieInfo
     * @returns
     */
    function addVideoPlayerElement(movieInfo) {
        debugger;
        if (!movieInfo.videoURL) return;
        let video;
        let muted = settings.enable_mute_play == true ? "muted" : "";
        //target video url need use iframe
        if (needCORS(movieInfo.videoURL)) {
            video = `
      <iframe id="preview-video-iframe" name="preview-video-iframe" width="60%" height="80%" style="border:none;border-radius:8px;" srcdoc="<html><head><style>*{width:100%;height:100%;padding:0;margin:0;overflow:hidden;} video{background: #000;}</style></head><body><video controls><source src='${movieInfo.videoURL}' type='video/mp4'></video></body></html>">
      </iframe>`;
        } else {
            video = `
        <video id="preview-video-player" playsinline controls preload="none" ${muted} poster="${
                document.querySelector("a.bigImage img")?.src
            }">
            <source src="${movieInfo.videoURL}" type="video/mp4" />
        </video>`;
            if (
                videojs &&
                ((settings.enable_vr_mode == true && movieInfo.isVR) ||
                    isM3U8(movieInfo.videoURL))
            ) {
                video = `
              <video id="preview-video-player" class="video-js" playsinline controls preload="none"
              ${muted} poster="${
                    document.querySelector("a.bigImage img")?.src
                }" crossorigin="anonymous">
              </video>`;
            }
        }
        let vContainer = document.createElement("div");
        vContainer.id = "preview-video-container";
        vContainer.innerHTML = video;

        //onclick event -- hide and pause player
        vContainer.addEventListener("click", (event) => {
            if (event?.target != vContainer) return;
            vContainer.style.display = "none";
            if (player) {
                player.pause();
            } else {
                //reset iframe to pause video
                if (needCORS(movieInfo.videoURL)) {
                    const iframe = document.querySelector("#preview-video-iframe");
                    let originIframe = iframe.cloneNode(true);
                    iframe.parentElement.append(originIframe);
                    iframe.remove();
                }
            }
            document.body.style.overflow = "auto";
        });
        document.body.append(vContainer);
        initVideoPlayer(movieInfo);
        addVideoPreviewImage();
    }

    function initVideoPlayer(movieInfo) {
        if (needCORS(movieInfo.videoURL)) {
            return;
        }
        if (
            videojs &&
            ((settings.enable_vr_mode == true && movieInfo.isVR) || isM3U8(movieInfo.videoURL))
        ) {
            player = videojs("preview-video-player", {
                playbackRates: [0.5, 1, 1.5, 2],
            });
            player.mediainfo = player.mediainfo || {};
            if (isM3U8(movieInfo.videoURL)) {
                player.src({
                    type: "application/x-mpegURL",
                    src: movieInfo.videoURL,
                });
            }
            //enabled vr plugin
            if (movieInfo.isVR) {
                player.src({
                    type: "video/mp4",
                    src: movieInfo.videoURL,
                });
                player.mediainfo.projection = "360";
                player.vr({projection: "AUTO"});
                player.vr().on("initialized", () => {
                    player.vr().camera.position.x = -1.3981591081738982;
                    player.vr().camera.position.y = 0.035304011118944253;
                    player.vr().camera.position.z = -0.7904654323761686;
                });
                player.tech(false); //tech() will log warning without any argument
            }
            player.defaultPlaybackRate(settings.video_playback_speed);
        } else {
            player = document.querySelector("#preview-video-player");
            player.playbackRate = settings.video_playback_speed;
        }
    }

    /**
     * Append video preview image to movie gallery
     * @returns
     */
    function addVideoPreviewImage() {
        let imgWaterFall = document.querySelector("#sample-waterfall");
        if (!imgWaterFall) {
            imgWaterFall = document.createElement("div");
            imgWaterFall.id = "sample-waterfall";
            let heading = document.createElement("h4");
            heading.innerText = "樣品圖像";
            const clearfix = document.getElementsByClassName("clearfix")[0];
            clearfix?.before(heading, imgWaterFall);
        }
        let previewImgSpan = document.createElement("span");
        previewImgSpan.classList.add("sample-box");
        let photoFrame = document.createElement("div");
        photoFrame.classList.add("photo-frame", "preview-video-img-container");
        let posterImg = document.querySelector("a.bigImage img");
        if (posterImg) {
            let img = document.createElement("img");
            img.src = posterImg.src;
            photoFrame.append(img);
        }
        previewImgSpan.append(photoFrame);
        imgWaterFall.prepend(previewImgSpan);

        //onclick event
        previewImgSpan.addEventListener("click", () => {
            let vContainer = document.querySelector("#preview-video-container");
            if (vContainer) {
                document.body.style.overflow = "hidden";
                vContainer.style.display = "flex";
                if (player) {
                    player.pause();
                    player.load();
                    player.play();
                    player.focus();
                }
            }
        });
    }

    //#endregion

    //#region VideoUrl
    // function getPostfixItem(postfixIndex) {
    //     let prefixArr = postfixConnfig["xxx"];
    //     var confg = prefixArr[postfixIndex];
    //
    //     return confg.prefix;
    // }
    //
    // function changePostfixItem(postfixIndex){
    //     postfixIndex++;
    //     if(postfixIndex >= 3) {
    //         postfixIndex = 0;
    //     }
    //
    //     return postfixIndex;
    // }

    async function getVideoURL_2(movieInfo) {
        let videoURL = await queryLocalCacheDB(movieInfo)
            .catch((e) => {
                log(e);
                return queryDMMVideoURL(movieInfo, undefined, true, 0);
            })
            .catch((e) => {
                log(e);
                //changePostfixItem();
                return queryDMMVideoURL(movieInfo, undefined, true, 1);
            })
            .catch((e) => {
                log(e);
                //changePostfixItem();
                return queryDMMVideoURL(movieInfo, undefined, true, 2);
            })
            .catch((e) => {
                log(e);
            });
        movieInfo.videoURL = convertHTTPToHTTPS(videoURL);
        return movieInfo;
    }

    async function getVideoURL(movieInfo) {
        let videoURL = await queryLocalCacheDB(movieInfo)
            .catch((e) => {
                log(e);
                return queryDMMVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                return queryDMMVideoURL(movieInfo, undefined, true);
            })
            .catch((e) => {
                log(e);
                return queryJavSpylVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                return queryPrestigeVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                return queryMGStageVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                //retry query (use movie title as keyword)
                return queryMGStageVideoURL(movieInfo, true);
            })
            .catch((e) => {
                log(e);
                return queryBasicUncensoredVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                return queryTokyoHotVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                return queryAVFantasyVideoURL(movieInfo);
            })
            .catch((e) => {
                log(e);
                return queryAVFantasyVideoURL(movieInfo, true);
            })
            // .catch((e) => {
            //   log(e);
            //   return queryJavDBVideoURL(movieInfo);
            // })
            .catch((e) => {
                log(e);
            });
        movieInfo.videoURL = convertHTTPToHTTPS(videoURL);
        return movieInfo;
    }

    async function queryBasicUncensoredVideoURL(movieInfo) {
        if (!movieInfo.isUncensored)
            return Promise.reject(
                "Query basic uncensored: This function only support uncensored movie."
            );
        let videoURLs;
        const qualityArr = ["720p.mp4", "1080p.mp4", "480p.mp4", "360p.mp4", "240p.mp4"];
        if (
            movieInfo.corpName === "カリビアンコム" ||
            movieInfo.corpName === "Caribbeancom"
        ) {
            //create different quality video urls.
            videoURLs = qualityArr.map(
                (quality) =>
                    `https://smovie.caribbeancom.com/sample/movies/${movieInfo.movieId}/${quality}`
            );
        } else if (movieInfo.corpName === "東京熱" || movieInfo.corpName === "TokyoHot") {
            videoURLs = [`https://my.cdn.tokyo-hot.com/media/samples/${movieInfo.movieId}.mp4`];
        } else if (movieInfo.corpName === "天然むすめ" || movieInfo.corpName === "10musume") {
            videoURLs = qualityArr.map(
                (quality) =>
                    `https://smovie.10musume.com/sample/movies/${movieInfo.movieId}/${quality}`
            );
        } else if (movieInfo.corpName === "一本道" || movieInfo.corpName === "1pondo") {
            videoURLs = qualityArr.map(
                (quality) =>
                    `https://smovie.1pondo.tv/sample/movies/${movieInfo.movieId}/${quality}`
            );
            videoURLs.push(
                `https://ppvclips02.aventertainments.com/01m3u8/1pon_${movieInfo.movieId}/1pon_${movieInfo.movieId}.m3u8`
            );
        } else if (
            movieInfo.corpName === "ワンピース" ||
            movieInfo.corpName === "オリエンタルドリーム" ||
            movieInfo.corpName === "OnePieceEntertainment" ||
            movieInfo.corpName === "OrientalDream"
        ) {
            videoURLs = [
                `https://ppvclips02.aventertainments.com/${movieInfo.movieId}/ts/${movieInfo.movieId}-m3u8-aapl.ism/manifest(format=m3u8-aapl).m3u8`,
            ];
        } else if (
            movieInfo.corpName === "パコパコママ" ||
            movieInfo.corpName === "pacopacomama"
        ) {
            videoURLs = qualityArr.map(
                (quality) =>
                    `https://fms.pacopacomama.com/hls/sample/pacopacomama.com/${movieInfo.movieId}/${quality}`
            );
        } else {
            return Promise.reject(
                "Query basic uncensored: This function not support this corporation movie."
            );
        }
        for (const videoURL of videoURLs) {
            log("Query basic uncensored:\r\n" + videoURL);
            const validatedURL = await xFetch(videoURL, {method: "head"})
                .then((resp) => {
                    if (resp?.status === 200) {
                        log("Query basic uncensored: server result video url: " + videoURL);
                        return videoURL;
                    }
                    return null;
                })
                .catch((e) => {
                    log(e);
                    return null;
                });
            if (validatedURL) {
                return Promise.resolve(validatedURL);
            }
        }
        return Promise.reject("Query basic uncensored: Not found movie.");
    }

    async function queryJavSpylVideoURL(movieInfo) {
        //Video links for these companies require cors, so ignore it.
        // if (
        //   movieInfo.corpName === "HEYZO" ||
        //   movieInfo.corpName === "一本道" ||
        //   movieInfo.corpName === "1pondo"
        // ) {
        //   return Promise.reject("JavSpyl server not support this corporation movie.");
        // }
        //see https://bit.ly/3RkgqSo
        let serverURL = "https://v2.javspyl.tk/api/";
        return await xFetch(serverURL, {
            headers: {
                origin: "https://v2.javspyl.tk",
                "Content-Type": "application/x-www-form-urlencoded",
            },
            data: `ID=${movieInfo.movieId}`,
            method: "POST",
        })
            .then((resp) => {
                return JSON.parse(resp.responseText);
            })
            .then((video) => {
                if (video?.info?.url?.length > 0) {
                    log("JavSpyl server result video url: https://" + video.info.url);
                    return "https://" + video.info.url;
                } else {
                    return Promise.reject("JavSpyl server not found movie.");
                }
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryAVFantasyVideoURL(movieInfo, isStandbyServer = false) {
        if (!movieInfo.isUncensored)
            return Promise.reject("AVFantasyDMM server only support uncensored movie.");
        let notFound = Promise.reject("AVFantasy server not found movie.");
        let keyword = movieInfo.movieId;
        //Movie codes for these companies are not supported, so use movie titles to search.
        if (movieInfo.corpName === "HEYZO") {
            keyword = movieInfo.titleKeyPhrase;
        }
        let serverURL = `https://www.avfantasy.com/ppv/ppv_searchproducts.aspx?keyword=${keyword}`;
        if (isStandbyServer) {
            serverURL = `https://www.avfantasy.com/search_Products.aspx?keyword=${keyword}`;
        }
        log("AVFantasyDMM server query:\r\n" + serverURL);
        return await xFetch(serverURL)
            .then((response) => {
                //AVFantasy search result, may contain multiple movies.
                let doc = convertTextToDOM(response.responseText);
                let resultMovies = doc.querySelectorAll(".single-slider-product");
                if (!resultMovies) return notFound;
                let targetMovieEle = null;
                for (const element of resultMovies.values()) {
                    if (matchMovieByKeyword(element.innerHTML, movieInfo)) {
                        targetMovieEle = element;
                        break;
                    }
                }
                if (!targetMovieEle) return notFound;
                let movieDetailPathName = targetMovieEle.querySelector("a")?.href;
                if (!movieDetailPathName) return notFound;
                return xFetch(movieDetailPathName);
            })
            .then((response) => {
                //AVFantasy movie detail page result.
                let doc = convertTextToDOM(response.responseText);
                let videoSource = doc.querySelector("video source");
                if (videoSource && videoSource?.src) {
                    log("AVFantasy server result video url: " + videoSource.src);
                    return videoSource.src;
                }
                return notFound;
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryJavDBVideoURL(movieInfo) {
        let serverURL = `https://javdb.com/search?q=${movieInfo.movieId}&f=all`;
        let notFound = Promise.reject("JavDB server not found movie.");
        log("JavDB server query:\r\n" + serverURL);
        return await fetch(serverURL)
            .then((resp) => {
                return resp.text();
            })
            .then((text) => {
                //JavDB search result, may contain multiple movies.
                let doc = convertTextToDOM(text);
                let resultMovies = doc.querySelectorAll(".item");
                let targetMovieEle = null;
                for (const element of resultMovies.values()) {
                    if (matchMovieByKeyword(element.innerHTML, movieInfo)) {
                        targetMovieEle = element;
                        break;
                    }
                }
                if (!targetMovieEle) return notFound;
                let avDetailPathName = targetMovieEle.querySelector("a")?.pathname;
                if (!avDetailPathName) return notFound;
                return fetch("https://javdb.com" + avDetailPathName);
            })
            .then((avDetailResp) => {
                return avDetailResp.text();
            })
            .then((text) => {
                //JavDB movie detail page result.
                let doc = convertTextToDOM(text);
                let videoSource = doc.querySelector("#preview-video source");
                if (videoSource && videoSource?.src && videoSource.src != location.href) {
                    log("JavDB server result video url: " + videoSource.src);
                    return videoSource.src;
                }
                return notFound;
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryDMMVideoURL(
        movieInfo,
        host = "cc3001.dmm.co.jp",
        hasPrefix = false,
        prefixIndex = 0
    ) {
        if (movieInfo.isUncensored)
            return Promise.reject("DMM server not support uncensored movie.");
        if (movieInfo.thumbnailURL?.includes("mgstage.com"))
            return Promise.reject("DMM server not support MGStage movie.");
        //see https://www.javbus.com/forum/forum.php?mod=viewthread&tid=63374
        //see https://bit.ly/3wXLj6T
        let infix = "litevideo/freepv";
        //1500kbps = _dmb_w || 3000kbps = _mhb_w || vrlite || _sm_w.mp4 || _dm_w.mp4 || _dmb_s.mp4!!
        let postfix = "_dmb_w";
        if (movieInfo.isVR) {
            postfix = "vrlite";
            infix = "vrsample";
        }

        let movieIdSplit = movieInfo.movieId.toLowerCase().split("-");
        let corp = movieIdSplit[0];
        let idNum = movieIdSplit[1];

        let videoURL = `https://${host}/${infix}/${corp[0]}/${corp.substring(
            0,
            3
        )}/${corp}${idNum}/${corp}${idNum}${postfix}.mp4`;

        if (hasPrefix === false && movieInfo.thumbnailURL?.includes("pics.dmm.co.jp")) {
            //extract keyword from thumbnail
            //example: https://pics.dmm.co.jp/digital/video/mkmp00497/mkmp00497jp-2.jpg
            //result keyword: mkmp00497
            let keywordFromThumbnail = movieInfo.thumbnailURL
                ?.split("video/")[1]
                ?.split("/")[0];
            //validation
            if (keywordFromThumbnail?.includes(corp) && keywordFromThumbnail?.includes(idNum)) {
                videoURL = `https://${host}/${infix}/${
                    keywordFromThumbnail[0]
                }/${keywordFromThumbnail.substring(
                    0,
                    3
                )}/${keywordFromThumbnail}/${keywordFromThumbnail}${postfix}.mp4`;
            }
        }

        if (hasPrefix === true) {
            var preNum = "";
            var preCorp = "";

            if (corporations[corp]) {
                //There must first get the idNum, and then get corp. Because corp will change.
                preNum = corporations[corp][1] ? corporations[corp][1] : "";
                preCorp = corporations[corp][0];
            } else {
                //if last time query is fail, this time try to add '00' prefix
                if (!movieInfo.thumbnailURL?.includes("pics.dmm.co.jp")) {
                    preNum = "00";
                }
            }

            if (postfixConnfig[corp]) {
                if (postfixConnfig[corp][prefixIndex]) {
                    var confg = postfixConnfig[corp][prefixIndex];
                    // if (confg.num <= parseInt(idNum)){
                    //     if (confg.prefix != null){
                    //         postfix = confg.prefix;
                    //     }
                    //
                    //     if (confg.prenum != null){
                    //         preNum = confg.prenum;
                    //     }
                    //
                    //     if (confg.precorp != null){
                    //         preCorp = confg.precorp;
                    //     }
                    //
                    // }else {
                    //     return Promise.reject("num not pipei.");
                    // }

                    if (confg.prefix != null) {
                        postfix = confg.prefix;
                    }

                    if (confg.prenum != null) {
                        preNum = confg.prenum;
                    }

                    if (confg.precorp != null) {
                        preCorp = confg.precorp;
                    }
                } else {
                    return Promise.reject("prefixIndex is empty.");
                }
            } else if (postfixConnfig["xxx"]) {
                if (postfixConnfig["xxx"][prefixIndex]) {
                    var confg = postfixConnfig["xxx"][prefixIndex];
                    if (confg.prefix != null) {
                        postfix = confg.prefix;
                    }
                }
            } else if (prefixIndex > 0) {
                return Promise.reject("postfixConnfig is empty.");
            }

            idNum = preNum + idNum;
            corp = preCorp + corp;
            videoURL = `https://${host}/${infix}/${corp[0]}/${corp.substring(
                0,
                3
            )}/${corp}${idNum}/${corp}${idNum}${postfix}.mp4`;
        }

        log("DMM server query:\r\n" + videoURL);

        return await fetch(videoURL, {
            method: "head",
            referrerPolicy: "no-referrer",
        })
            .then((resp) => {
                if (resp.ok) {
                    log("DMM server result video url: " + videoURL);
                    GM_setValue(movieInfo.movieId, videoURL);
                    return videoURL;
                } else {
                    return Promise.reject("DMM server not found movie.");
                }
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryMGStageVideoURL(movieInfo, isUseTitle = false) {
        if (
            movieInfo.isUncensored ||
            (movieInfo.corpName.includes("プレステージ") === false &&
                movieInfo.corpName.includes("ラグジュTV") === false &&
                movieInfo.corpName.toUpperCase().includes("PRESTIGE") === false &&
                !movieInfo.thumbnailURL?.includes("mgstage.com") &&
                !movieInfo.thumbnailURL?.includes("prestige-av.com"))
        )
            return Promise.reject(
                `MGStage server not support movieId: ${movieInfo.movieId}, CorpName: ${movieInfo.corpName}`
            );
        let notFound = () => Promise.reject("MGStage server not found movie.");
        //Need ladder
        let keyword = isUseTitle ? movieInfo.titleKeyPhrase : movieInfo.movieId;
        let serverURL = `https://www.mgstage.com/search/cSearch.php?search_word=${keyword}&list_cnt=30`;
        log("MGStage server query:\r\n" + serverURL);
        return await xFetch(serverURL)
            .then((resp) => {
                //MGStage search result, may contain multiple movies.
                let doc = convertTextToDOM(resp.responseText);
                let resultMovies = doc.querySelectorAll(".rank_list li");
                let targetMovieEle = null;
                for (const element of resultMovies.values()) {
                    if (matchMovieByKeyword(element.innerHTML, movieInfo)) {
                        targetMovieEle = element;
                        break;
                    }
                }
                if (!targetMovieEle) return notFound();
                let avDetailPathName = targetMovieEle.querySelector(".button_sample")?.pathname;
                if (!avDetailPathName) return notFound();
                let pid = avDetailPathName.split("/").at(-1);
                return xFetch(
                    "https://www.mgstage.com/sampleplayer/sampleRespons.php?pid=" + pid
                );
            })
            .then((avDetailResp) => {
                //MGStage movie detail page result.
                let videoURL = JSON.parse(avDetailResp.response)?.url?.split(".ism")[0] + ".mp4";
                if (videoURL) {
                    log("MGStage server result video url: " + videoURL);
                    return videoURL;
                }
                return notFound();
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryPrestigeVideoURL(movieInfo, isUseTitle = false) {
        if (
            movieInfo.isUncensored ||
            (movieInfo.corpName.includes("プレステージ") === false &&
                movieInfo.corpName.toUpperCase().includes("PRESTIGE") === false &&
                !movieInfo.thumbnailURL?.includes("mgstage.com") &&
                !movieInfo.thumbnailURL?.includes("prestige-av.com"))
        )
            return Promise.reject(
                `Prestige server not support movieId: ${movieInfo.movieId}, CorpName: ${movieInfo.corpName}`
            );
        let notFound = () => Promise.reject("Prestige server not found movie.");
        //Need ladder
        let keyword = isUseTitle ? movieInfo.titleKeyPhrase : movieInfo.movieId;
        let serverURL = `https://www.prestige-av.com/api/search?isEnabledQuery=true&searchText=${keyword}&isEnableAggregation=false&release=false&reservation=false&soldOut=false&from=0&aggregationTermsSize=0&size=20`;
        log("Prestige server query:\r\n" + serverURL);
        return await xFetch(serverURL)
            .then((avDetailResp) => {
                let resultMovies = JSON.parse(avDetailResp.response)?.hits?.hits;
                for (const movieDetail of resultMovies) {
                    let {deliveryItemId, productTitle, productMovie} = movieDetail["_source"];
                    if (
                        matchMovieByKeyword(deliveryItemId + productTitle, movieInfo) &&
                        productMovie?.path
                    ) {
                        let videoURL = "https://www.prestige-av.com/api/media/" + productMovie.path;
                        log("Prestige server result video url: " + videoURL);
                        return videoURL;
                    }
                }
                return notFound();
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryTokyoHotVideoURL(movieInfo) {
        if (
            !movieInfo.isUncensored ||
            (movieInfo.corpName != "東京熱" && movieInfo.corpName.toUpperCase() != "TOKYOHOT")
        )
            return Promise.reject(
                `TokyoHot server not support movieId: ${movieInfo.movieId}, CorpName: ${movieInfo.corpName}`
            );
        let notFound = Promise.reject("TokyoHot server not found movie.");
        //Need ladder
        let serverURL = `https://my.cdn.tokyo-hot.com/product/?q=${movieInfo.movieId}`;
        log("TokyoHot server query:\r\n" + serverURL);
        return await xFetch(serverURL)
            .then((resp) => {
                //search result, may contain multiple movies.
                let doc = convertTextToDOM(resp.responseText);
                let resultMovies = doc.querySelectorAll(".list.slider.cf li");
                let targetMovieEle = null;
                for (const element of resultMovies.values()) {
                    if (matchMovieByKeyword(element.innerHTML, movieInfo)) {
                        targetMovieEle = element;
                        break;
                    }
                }
                if (!targetMovieEle) return notFound;
                let avDetailPathName = targetMovieEle.querySelector(".rm")?.pathname;
                if (!avDetailPathName) return notFound;
                return xFetch("https://my.cdn.tokyo-hot.com/" + avDetailPathName);
            })
            .then((resp) => {
                //detail page result
                let doc = convertTextToDOM(resp.responseText);
                let videoSource = doc.querySelector("video source");
                if (videoSource && videoSource?.src && videoSource.src != location.href) {
                    log("Tokyo server result video url: " + videoSource.src);
                    return videoSource.src;
                }
                return notFound;
            })
            .catch((e) => {
                return Promise.reject(e);
            });
    }

    async function queryLocalCacheDB(movieInfo) {
        let videoSource = GM_getValue(movieInfo.movieId, null);
        if (!videoSource) {
            return Promise.reject("Local cache storage not found movie.");
        }

        return Promise.resolve(videoSource);

        // return await xFetch(videoSource, { method: "head" })
        //     .then((resp) => {
        //         if (resp?.status === 200) {
        //             log("The video source URL validate successful. Video source: " + videoSource);
        //             return Promise.resolve(videoSource);
        //         }
        //         //If validation fails,remove the url from the local cache.
        //         GM_deleteValue(movieInfo.movieId);
        //         return Promise.reject("The video source URL validate failed. Not found movie.");
        //     })
        //     .catch((e) => {
        //         return Promise.reject(e);
        //     });
    }

    //#endregion

    //#region Utility

    async function xFetch(url, fetchInit = {}) {
        const defaultFetchInit = {method: "get"};
        const {headers, method, data} = {...defaultFetchInit, ...fetchInit};
        return new Promise((resolve, reject) => {
            void GM_xmlhttpRequest({
                url,
                method,
                headers,
                data,
                onerror: reject,
                onload: async (response) => resolve(response),
            });
        });
    }

    function convertTextToDOM(text) {
        return DOMPurify.sanitize(text);
    }

    function log(msg) {
        if (settings.enable_debug_mode == true) {
            if (typeof msg === "object") {
                console.dir(msg);
            } else {
                console.log(msg);
            }
        }
    }

    function isM3U8(url) {
        return url?.endsWith("m3u8");
    }

    function needCORS(url) {
        return need_cors_domain.has(new URL(url)?.host);
    }

    /**
     * Convert HTTP protocol to HTTPS protocol
     * @param {string} url
     * @returns {string} url
     */
    function convertHTTPToHTTPS(url) {
        if (url?.startsWith("http:")) {
            return url.replace("http:", "https:");
        }
        return url;
    }

    function matchMovieByKeyword(str, movieInfo) {
        if (str.indexOf(movieInfo.movieId) != -1 || str.indexOf(movieInfo.title) != -1) {
            return true;
        }
        if (str.indexOf(movieInfo.titleKeyPhrase) != -1) {
            return true;
        }
        if (findDifference(movieInfo.titleKeyPhrase, removeWhitespace(str)).length < 4) {
            return true;
        }
        return false;
    }

    function getKeyPhrase(str, splitChar = " ") {
        return str
            .split(splitChar)
            .reduce((pre, cur) => (cur?.length > pre.length ? cur : pre), "");
    }

    function findDifference(str1, str2) {
        const len = Math.min(str1.length, str2.length);
        const result = [];
        for (let i = 0; i < len; i++) {
            if (str1[i] !== str2[i]) {
                result.push(str1[i]);
                str1 = replaceChar(str1, i, str2[i]);
            }
        }
        return result;
    }

    function replaceChar(str, index, newChar) {
        const leftPart = str.slice(0, index);
        const rightPart = str.slice(index + 1);
        return leftPart + newChar + rightPart;
    }

    function removeWhitespace(str) {
        return str.replace(/\s/g, "");
    }

    //#endregion

    // ******************************************************************************************************************************************************
    class Common {

        static updateVideoAddrElement(pre_id, movieInfo) {
            var addr_id = "#" + pre_id + "_" + movieInfo.movieId;
            var addr = document.querySelector(addr_id);
            if (addr) {
                addr.href = movieInfo.videoURL;
                addr.textContent = movieInfo.videoURL;
            }
        }

        static async copyTextToClipboard(text) {
            try {
                await navigator.clipboard.writeText(text);
                console.log('-------复制成功！！！！！！！！！');
            } catch (err) {
                console.error('-------复制失败: ', err);
            }
        }
		
		static copyTextToClipboardForAaa(text) {
            var textArea = document.createElement("textarea");
            textArea.value = text;
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();

            try {
                var successful = document.execCommand('copy');
                var msg = successful ? 'successful' : 'unsuccessful';
                console.log('Fallback: Copying text command was ' + msg);
            } catch (err) {
                console.error('Fallback: Oops, unable to copy', err);
            }

            document.body.removeChild(textArea);
        }

        static getMovieInfo() {
            const infoDom = document.querySelector(".movie-panel-info");
            if (!infoDom) {
                console.log("getMovieInfo -------------- 失败!!!!");
                return;
            }
            let movieId = infoDom.innerText
                .match(/[A-Za-z]{2,5}-\d{2,5}/)
                ?.at(0);
            let corpName = infoDom.innerText
                .match(/(?<=(?:片商|Maker):.)[\x20\S]+/)
                ?.at(0)
                ?.trim();
            let isVR =
                infoDom
                    .querySelector("#genre-toggle")
                    ?.parentElement?.nextElementSibling.innerText.search(
                    /ハイクオリティVR|VR専用|カリVR|VR/
                ) > -1;
            let isUncensored =
                document
                    .querySelector("#navbar li.active")
                    ?.innerText.search(/無碼|Uncensored|無修正|무수정/) > -1;
            let isEuropeOrAmerica =
                document
                    .querySelector("#navbar li.active")
                    ?.innerText.search(/歐美|Western|外国人|서양의/) > -1;
            let title = document.querySelector(".current-title")?.innerText;
            let thumbnailURL = document.querySelector(".sample-box")?.href;
            let titleKeyPhrase = getKeyPhrase(title);
            let videoURL = "www";
            let isPlay = false;

            //retry extract
            if (titleKeyPhrase === title) titleKeyPhrase = getKeyPhrase(title, "！！");

            log({
                title,
                movieId,
                corpName,
                isVR,
                isUncensored,
                isEuropeOrAmerica,
                thumbnailURL,
                titleKeyPhrase,
                videoURL,
                isPlay
            });
            return {
                title,
                movieId,
                corpName,
                isVR,
                isUncensored,
                isEuropeOrAmerica,
                thumbnailURL,
                titleKeyPhrase,
                videoURL,
                isPlay
            };
        }

        /**
         * Append player style
         */
        static addPreviewVideoStyle() {
            let containerStyle = `
        #preview-video-container{
            position: fixed;
            height: 100%;
            width: 100%;
            background-color: rgba(0,0,0,0.8);
            top: 0px;
            z-index: 999;
            display: none;
            align-items: center;
            justify-content: center;
        }
        #preview-video-container:before {
            content: '\\2715';
            font-size: 28px;
            color: white;
            opacity: 0.7;
            right: 18px;
            top: 8px;
            position: absolute;
            cursor: pointer;
            pointer-events:none;
        }
        #preview-video-player{
            height: 80%;
            min-width: 70%;
            max-width: 80%;
            background-color: #000;
            border-radius: 8px;
            outline: none;
            overflow: hidden;
        }
        .preview-video-img-container{
            position: relative;
            cursor:pointer;
        }
        .preview-video-img-container img{
            max-width: 100%;
            object-fit: cover;
            width: 120px;
        }
        .preview-video-img-container:after {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'  viewBox='0 0 512 512'%3E%3Cpath d='M448 255c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z' fill='gold'  fill-opacity='0.8' stroke='none'/%3E%3Cpath fill='white' d='M216.32  334.44l114.45-69.14a10.89 10.89 0 000-18.6l-114.45-69.14a10.78 10.78 0 00-16.32 9.31v138.26a10.78 10.78 0 0016.32 9.31z'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: 50%;
            background-color: #0000005e;
            background-size: 48px 48px;
            bottom: 0;
            content: "";
            display: block;
            left: 0;
            position: absolute;
            right: 0;
            top: 0;
        }
        `;
            GM_addStyle(containerStyle);
            GM_addStyle(GM_getResourceText("video-js-css"));
            GM_addStyle(GM_getResourceText("video-vr-js-css"));
        }

        static createMovieInfo() {

            let movieId = "DVDES-101";
            let corpName = "unknown";
            let isVR = false;
            let isUncensored = false;
            let isEuropeOrAmerica = false;
            let title = "unknown";
            let thumbnailURL = "unknown";
            let titleKeyPhrase = "unknown";
            let videoURL = "http://xxx/";
            let webURL = "http://xxx/";

            // log({
            //     title,
            //     movieId,
            //     corpName,
            //     isVR,
            //     isUncensored,
            //     isEuropeOrAmerica,
            //     thumbnailURL,
            //     titleKeyPhrase,
            // });
            return {
                title,
                movieId,
                corpName,
                isVR,
                isUncensored,
                isEuropeOrAmerica,
                thumbnailURL,
                titleKeyPhrase,
                videoURL,
                webURL
            };
        }

        static loadMovieInfo() {
            // document.getElementById("demo").innerHTML=Date();

            const movieInfo = Common.getMovieInfo();
            if (movieInfo?.movieId) {
                let movieIdSplit = movieInfo.movieId.toLowerCase().split("-");
                let corp = movieIdSplit[0];
                let idNum = movieIdSplit[1];
                let num = parseInt(idNum);

                document.getElementById("movieId-corp").value = corp;
                document.getElementById("movieId-start").value = num - 10;
                document.getElementById("movieId-end").value = num;

            }
        }

        static padNumber(num, size) {
            var s = num + ""; // 将数字转换为字符串
            while (s.length < size) s = "0" + s; // 如果字符串长度小于指定大小，则在前面添加0
            return s;
        }

        static sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }

        static concatenateChineseCharacters(str) {
            let strArr = str.split(" ");
            // 使用正则表达式匹配汉字
            // 这里只考虑了基本汉字范围\u4e00-\u9fa5
            const chineseCharsRegex = /[\u4e00-\u9fa50-9A-Zの=]+/g;

            // 使用matchAll方法（注意：在某些旧版JavaScript环境中可能需要polyfill或使用match然后处理结果）
            // matchAll返回一个迭代器，包含所有匹配项及其相关信息
            const matches = [...strArr[0].matchAll(chineseCharsRegex)];

            // 遍历匹配项，并提取出匹配的汉字字符串
            // 由于我们使用的是+量词，每个匹配项都是一个或多个连续的汉字
            let concatenatedChinese = '';
            // matches.forEach(match => {
            //     concatenatedChinese += "、" + match[0]; // match[0]是匹配的整个字符串
            // });
            for (let i = 0; i < matches.length; i++) {
                if (i == 0) {
                    concatenatedChinese += matches.at(i)[0]; // match[0]是匹配的整个字符串
                } else {
                    concatenatedChinese += "、" + matches.at(i)[0]; // match[0]是匹配的整个字符串
                }
            }
            concatenatedChinese += "=" + strArr[1];

            return concatenatedChinese;
        }

        static concatenateChineseCharacters_2(str) {
            // let strArr = str.trim().split(' ');

            const regex = /[\s,!?、，！？」】●]+/g;
            let strArr2 = str.trim().split(regex);

            let concatenatedChinese = '';
            if (strArr2.length > 0) {
                concatenatedChinese += strArr2[0].trim();
            }

            // if (strArr.length >= 2){
            //     concatenatedChinese += "==" + strArr[strArr.length - 1];
            // }

            return concatenatedChinese;
        }

        static parseYanYuan(yanYuanStr) {
            let yanYanArr = yanYuanStr.trim().split(' ');
            let nvYanYuan = '';

            for (let i = 0; i < yanYanArr.length; i++) {
                let yan = yanYanArr[i];

                if (yan.lastIndexOf('♀') >= 0) {
                    let re = (i == 0) ? '==' : '、';
                    nvYanYuan += re + yan.replace('♀', '').trim();
                }
            }

            return nvYanYuan;
        }

        // 定义一个函数来获取选择的文本
        static getSelectedText() {
            // 使用window.getSelection()获取Selection对象
            const selection = window.getSelection();

            // 检查是否有选择的文本
            if (selection.rangeCount > 0) {
                // 获取第一个（也可能是唯一的）Range对象
                const range = selection.getRangeAt(0);

                // 使用Range对象的toString方法获取选择的文本
                // 注意：这里假设选择的是纯文本，没有包含其他元素或富文本格式
                const selectedText = range.toString();

                // 返回选择的文本
                return selectedText;
            }

            // 如果没有选择的文本，返回null或空字符串
            return null; // 或者 return '';
        }

        static popupWindow(newUrl, width = 1420, height = 960) {
            var left = (window.screen.width - width) / 2;
            var top = (window.screen.height - height) / 2;
            var popupWindow = window.open(newUrl, '_blank', `left=${left}, top=${top} width=${width}, height=${height}`);
            // popupWindow.location.href = newUrl;
        }

    }

    // ******************************************************************************************************************************************************
    // 主页-预告video
    class PreVideoHome {
        static createVideoCss() {
            // 创建一个新的<style>元素
            const style = document.createElement('style');
            style.type = 'text/css';

            // 添加CSS规则到<style>元素中
            const cssRules = `
      .box .cover video {
            z-index: 0;
            transition: transform .2s ease;
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            /*-o-object-position: 50% 50%;*/
            /*object-position: 50% 50%;*/
        }
    `;

            if (style.styleSheet) {
                // 对于IE
                style.styleSheet.cssText = cssRules;
            } else {
                // 其他浏览器
                style.appendChild(document.createTextNode(cssRules));
            }

// 将<style>元素添加到文档的<head>中
            document.head.appendChild(style);
        }

        static initVideoList() {
            let boxs = document.querySelectorAll('a.box');
            if (boxs) {
                for (let index = 0; index < boxs.length; index++) {
                    let box = boxs[index];

                    let titleCode = box.querySelector('.video-title strong');
                    let text = titleCode.innerText;
                    let code = text.match(/[A-Za-z]{2,5}-\d{2,5}/)?.at(0);
                    if (!code) continue;

                    let videoSrc = "https://fivetiu.com/" + code.toLowerCase() + "/preview.mp4";
                    let video = document.createElement('video');
                    video.style.display = "none";
                    video.autoplay = true;
                    video.loop = true;
                    video.muted = true;
                    video.playsinline = true;
                    video.dataset.src = videoSrc;
                    // video.src =videoSrc;

                    let cover = box.querySelector('.cover');
                    if (!cover) continue;

                    cover.appendChild(video);
                }
            }
        }

        static addMouseListener() {
            // document.addEventListener('DOMContentLoaded', function() {
            const boxs = document.querySelectorAll('.box .cover');
            if (!boxs) return;

            for (let index = 0; index < boxs.length; index++) {
                let cover = boxs[index];

                cover.parentElement.removeAttribute('title');

                cover.addEventListener('mouseenter', function (event) {
                    // if (event.target.classList.contains('cover')) {
                    const videoContainer = event.target;
                    const video = videoContainer.querySelector('video');
                    const image = videoContainer.querySelector('img');
                    video.style.display = 'block';
                    image.style.display = 'none';

                    if (!video['src']) {
                        video.src = video.dataset.src;
                    }
                    image.removeAttribute('alt');
                    // }
                });

                cover.addEventListener('mouseleave', function (event) {
                    // if (event.target.classList.contains('cover')) {
                    const videoContainer = event.target;
                    const video = videoContainer.querySelector('video');
                    const image = videoContainer.querySelector('img');
                    video.style.display = 'none';
                    image.style.display = 'block';
                    // }
                });
                // });
            }
        }

        static async initVideoListForItem() {
            let all_flag = null;
            try {
                let url = 'http://localhost:10168/allflag';

                const response = await fetch(url);
                if (!response.ok) {
                    throw new Error('HTTP错误! 状态: ${response.status}');
                }
                const result = await response.json();
                if (result.result != 0) {
                    throw new Error('HTTP错误! 状态: ${result.desc}');
                }

                all_flag = result.data;

            } catch (error) {
                console.log('get all flag error:' + error);
            }

            // xxx
            let boxs = document.querySelectorAll('a.box');
            if (boxs) {
                for (let index = 0; index < boxs.length; index++) {
                    let box = boxs[index];

                    let link = box.href;
                    let titleEle = box.querySelector('.video-title');
                    let titleStr = titleEle.innerText;
                    let titleCode = titleEle.querySelector('strong');
                    let text = titleCode.innerText;
                    let code = text.match(/[A-Za-z]{2,5}-\d{2,5}/)?.at(0);
                    if (!code) continue;

                    let cover = box.querySelector('.cover');
                    if (!cover) continue;

                    let yulan = box.querySelector('.tags.has-addons a[title="预览"]');
                    if (yulan) {
                        yulan.addEventListener('click', function (event) {
                            let videoSrc = "https://fivetiu.com/" + code.toLowerCase() + "/preview.mp4";
                            let video = document.createElement('video');
                            video.style.display = "none";
                            video.autoplay = true;
                            video.loop = true;
                            video.muted = true;
                            video.playsinline = true;
                            video.dataset.src = videoSrc;
                            // video.src =videoSrc;

                            cover.appendChild(video);

                            PreVideoHome.addMouseListenerForItem(cover);
                        })
                    }

                    let yulanTu = box.querySelector('.tags.has-addons a[title="预览图"]');
                    if (yulanTu) {
                        yulanTu.addEventListener('click', function (event) {
                            let imgUrl = "https://blogjav.net/?s=" + code.toLowerCase() + "&jump=auto";
                            // window.open(imgUrl, '_blank');
                            Common.popupWindow(imgUrl);
                        })
                    }

                    let javStoryTu = box.querySelector('.tags.has-addons a[title="综合 JAV资源站"]');
                    if (javStoryTu) {
                        javStoryTu.addEventListener('click', function (event) {
                            // var newUrl = 'http://localhost:10168/premovie/index.html?avcode=' + code.toLowerCase() + '&avtitle=' + titleStr + '&avurl=' + link + '&tab=2';
                            var newUrl = 'http://localhost:10168/premovie/index.html?avcode=' + code.toLowerCase() + '&avtitle=' + titleStr + '&avurl=' + link;
                            // var width = 1420;
                            // var height = 960;
                            // var left = (window.screen.width - width)/2;
                            // var top = (window.screen.height - height)/2;
                            // var popupWindow = window.open(newUrl, '_blank', `left=${left}, top=${top} width=${width}, height=${height}`);

                            Common.popupWindow(newUrl);
                        })
                    }

                    if (all_flag) {
                        let flag = all_flag[code];
                        if (flag) {
                            let pinlEle = box.querySelector('.value');
                            pinlEle.style = 'color: red';
                            // 保存子元素的HTML
                            const childrenHTML = pinlEle.innerHTML;
                            pinlEle.innerHTML = childrenHTML + ' &nbsp;【【' + flag + '】】';
                        }
                    }
                }
            }
        }

        static addMouseListenerForItem(cover) {
            // #fff-amlfi
            cover.parentElement.removeAttribute('title');

            cover.addEventListener('mouseenter', function (event) {
                // if (event.target.classList.contains('cover')) {
                const videoContainer = event.target;
                const video = videoContainer.querySelector('video');
                const image = videoContainer.querySelector('img');
                video.style.display = 'block';
                image.style.display = 'none';

                if (!video['src']) {
                    video.src = video.dataset.src;
                }
                image.removeAttribute('alt');
                // }
            });

            cover.addEventListener('mouseleave', function (event) {
                // if (event.target.classList.contains('cover')) {
                const videoContainer = event.target;
                const video = videoContainer.querySelector('video');
                const image = videoContainer.querySelector('img');
                video.style.display = 'none';
                image.style.display = 'block';
                // }
            });

        }
    }
    //

    // ******************************************************************************************************************************************************
    // 详情页-预告Video
    class PreVideoDetail {
        static addMoivePreVideo() {
            var contDiv = document.getElementsByClassName("preview-images")[0];
            if (!contDiv) return;

            /* 预览video */
            const movieInfo = Common.getMovieInfo();
            if (movieInfo?.movieId) {
                let text = movieInfo.movieId.toLowerCase();
                let code = text.match(/[A-Za-z]{2,5}-\d{2,5}/)?.at(0);
                if (!code) return;
                ;

                let videoSrc = "https://fivetiu.com/" + code.toLowerCase() + "/preview.mp4";

                var preVideo = document.createElement("video");
                preVideo.style = `
                position: absolute;
                top: 0;
                left: 0;
                height: 100%;
            `;
                preVideo.controls = true;
                preVideo.loop = true;
                preVideo.muted = true;
                preVideo.playsinline = true;
                preVideo.src = videoSrc;

                /* 按钮面板 */
                var videoDiv = document.createElement("div");
                videoDiv.style = `
                position: relative;
                padding-top: 35%;
                margin: 20px;
            `;
                videoDiv.appendChild(preVideo);

                /* 插入到页面中 */
                contDiv.parentNode.insertBefore(videoDiv, contDiv);
            }
        }

        static createVideoCss_2() {
            // 创建一个新的<style>元素
            const style = document.createElement('style');
            style.type = 'text/css';

            // 添加CSS规则到<style>元素中
            const cssRules = `
                  .video-panel .tile-images video {
                        z-index: 0;
                        transition: transform .2s ease;
                        position: absolute;
                        top: 0;
                        left: 0;
                        height: 100%;
                        /*-o-object-position: 50% 50%;*/
                        /*object-position: 50% 50%;*/
                    }
                `;

            if (style.styleSheet) {
                // 对于IE
                style.styleSheet.cssText = cssRules;
            } else {
                // 其他浏览器
                style.appendChild(document.createTextNode(cssRules));
            }

            // 将<style>元素添加到文档的<head>中
            document.head.appendChild(style);
        }

        static resetPreVideoBig() {
            var preImgConEles = document.querySelectorAll('.tile-images.tile-small');
            if (!preImgConEles) return;

            var style1 = document.createElement('style');
            style1.innerHTML = `
          .tile-images-3 {
                display: grid;
                grid-column-gap: 0.2rem;
                -moz-column-gap: .2rem;
                column-gap: 0.2rem;
                grid-row-gap: 0.4rem;
                row-gap: 0.4rem;
                grid-template-columns: repeat(3,minmax(0,1fr));
            }
        `;
            document.body.appendChild(style1);

            for (let index = 0; index < preImgConEles.length; index++) {
                let preImgConEle = preImgConEles[index];

                preImgConEle.classList.remove("tile-images");
                preImgConEle.classList.add("tile-images-3");
            }
        }

        static initVideoList_2() {
            let videoNumbers = document.querySelectorAll('.video-number');
            if (videoNumbers) {
                for (let index = 0; index < videoNumbers.length; index++) {
                    let videoNumber = videoNumbers[index];
                    let box = videoNumber.parentElement;
                    if (!box) continue;

                    let text = videoNumber.innerText;
                    let code = text.match(/[A-Za-z]{2,5}-\d{2,5}/)?.at(0);
                    if (!code) continue;

                    let videoSrc = "https://fivetiu.com/" + code.toLowerCase() + "/preview.mp4";
                    let video = document.createElement('video');
                    // video.style.display = "none";
                    video.style = `
                    top: 0;
                    left: 0;
                    width: 100%;
                    height: 100%;
                    display: none;
                `;
                    video.autoplay = true;
                    video.loop = true;
                    video.muted = true;
                    video.playsinline = true;
                    video.dataset.src = videoSrc;
                    // video.src =videoSrc;

                    box.appendChild(video);
                }
            }
        }

        static addMouseListener_2() {
            // document.addEventListener('DOMContentLoaded', function() {
            let videoNumbers = document.querySelectorAll('.video-number');
            if (!videoNumbers) return;

            for (let index = 0; index < videoNumbers.length; index++) {
                let videoNumber = videoNumbers[index];
                let box = videoNumber.parentElement;
                if (!box) continue;

                box.removeAttribute('title');

                box.addEventListener('mouseenter', function (event) {
                    // if (event.target.classList.contains('cover')) {
                    const videoContainer = event.target;
                    const video = videoContainer.querySelector('video');
                    const image = videoContainer.querySelector('img');
                    const title = videoContainer.querySelector('.video-title');
                    const avnumber = videoContainer.querySelector('.video-number');
                    video.style.display = 'block';
                    image.style.display = 'none';
                    title.style.display = 'none';
                    avnumber.style.display = 'none';

                    if (!video['src']) {
                        video.src = video.dataset.src;
                    }
                    image.removeAttribute('alt');
                    // }
                });

                box.addEventListener('mouseleave', function (event) {
                    // if (event.target.classList.contains('cover')) {
                    const videoContainer = event.target;
                    const video = videoContainer.querySelector('video');
                    const image = videoContainer.querySelector('img');
                    const title = videoContainer.querySelector('.video-title');
                    const avnumber = videoContainer.querySelector('.video-number');
                    video.style.display = 'none';
                    image.style.display = 'block';
                    title.style.display = 'block';
                    avnumber.style.display = 'block';
                    // }
                });
                // });
            }
        }

    }

    // ******************************************************************************************************************************************************
    class BottomPanel {

        static addButtonBottomList() {
            // var para = document.createElement("button");
            // var node = document.createTextNode("新的按钮");
            // para.appendChild(node);
            // para.addEventListener("click", testButton01);

            /* 番号范围面板 */
            var ppp1 = document.createElement("p");
            ppp1.innerHTML = `
                    <span>番号范围：</span>
                    <input id="movieId-corp" type="text" style="width: 80px"/>
                    <span> - </span>
                    <input id="movieId-start" type="number" style="width: 60px"/>
                    <span> >>> </span>
                    <input id="movieId-end" type="number" style="width: 60px"/>
                   `;


            /* 提交番号范围 */
            var btnQuery = document.createElement("button");
            btnQuery.appendChild(document.createTextNode("提交"));
            btnQuery.addEventListener("click", function (){
                BottomPanel.queryMovieAddrList();
            });
            ppp1.appendChild(btnQuery);

            var ppp2 = document.createElement("p");
            // ppp2.innerHTML = `
            //             <button onclick="loadMovieInfo()">加载</button>
            //             <button onclick="clearMovieAddrListBottom()">清除</button>
            //            `;
            /* 相似番号 */
            var btnLoad = document.createElement("button");
            btnLoad.style.marginRight = "10px";
            btnLoad.appendChild(document.createTextNode("相同番号"));
            btnLoad.addEventListener("click", function (){
                Common.loadMovieInfo();
            });
            ppp2.appendChild(btnLoad);

            /* 同类番号 */
            var btnClass = document.createElement("button");
            btnClass.style.marginLeft = "10px";
            btnClass.style.marginRight = "10px";
            btnClass.appendChild(document.createTextNode("同类&主演影片"));
            btnClass.addEventListener("click", function (){
                BottomPanel.queryDetailPageAddrList();
            });
            ppp2.appendChild(btnClass);

            /* 同类番号 */
            var btnClassVideo = document.createElement("button");
            btnClassVideo.style.marginLeft = "10px";
            btnClassVideo.style.marginRight = "10px";
            btnClassVideo.appendChild(document.createTextNode("同类&主演video"));
            btnClassVideo.addEventListener("click", function () {
                //PreVideoDetail.createVideoCss_2();
                PreVideoDetail.resetPreVideoBig();
                PreVideoDetail.initVideoList_2();
                PreVideoDetail.addMouseListener_2();
            });
            ppp2.appendChild(btnClassVideo);

            /* 清除列表 */
            var btnClear = document.createElement("button");
            btnClear.style.marginLeft = "30px";
            btnClear.style.marginRight = "10px";
            btnClear.appendChild(document.createTextNode("清除"));
            btnClear.addEventListener("click", function (){
                BottomPanel.clearMovieAddrListBottom();
            });
            ppp2.appendChild(btnClear);

            /* 播放地址列表 */
            var addrdiv = document.createElement("div");
            addrdiv.className = "movie";
            addrdiv.id = "addr-list-bottom";

            /* 插入到页面中 */
            var element = document.getElementsByClassName("video-detail")[0];
            // element.appendChild(para);
            element.appendChild(ppp2);
            element.appendChild(ppp1)
            element.append(addrdiv);
        }

        static queryMovieAddrList() {
            const movieInfoArr = new Array();
            let corp = document.getElementById("movieId-corp").value;
            let startIndex = parseInt(document.getElementById("movieId-start").value);
            let endIndex = parseInt(document.getElementById("movieId-end").value);
            let len = endIndex - startIndex;

            for (var i = 0; i <= len; i++) {
                let movieInfo = Common.getMovieInfo();
                let index = startIndex + i;
                movieInfo.movieId = corp + "-" + Common.padNumber(index, 3);
                movieInfoArr[i] = movieInfo
                if (movieInfoArr[i]?.movieId) {
                    addVideoAddrElementBottom("sreach_addr", movieInfo);
                    getVideoURL_2(movieInfoArr[i]).then((movieInfo) => {
                        if (!movieInfo.videoURL) return;

                        console.log(movieInfo.videoURL)
                        Common.updateVideoAddrElement("sreach_addr", movieInfo);
                    });
                }
            }
        }

        static clearMovieAddrListBottom() {
            var ddd = document.getElementById("addr-list-bottom");
            ddd.innerHTML = "";
        }

        static addVideoAddrElementBottom(pre_id, movieInfo) {
            var para = document.createElement("p");

            const newCheckbox = document.createElement('input');
            newCheckbox.type = 'checkbox'; // 设置类型为checkbox

            var addr = document.createElement("a");
            addr.id = pre_id + "_" + movieInfo.movieId;
            addr.href = movieInfo.videoURL;
            addr.target = "_blank";
            var text = document.createTextNode(movieInfo.videoURL);
            addr.append(text);

            var title = document.createElement("a");
            title.style.color = "#e5a555"
            title.href = movieInfo.webURL;
            title.target = "_blank";
            var text = document.createTextNode("***【" + movieInfo.movieId + "】***\t");
            title.append(text);

            para.appendChild(newCheckbox);
            para.appendChild(title)
            para.appendChild(addr);

            var element = document.getElementById("addr-list-bottom");
            if (element) {
                element.appendChild(para);
            }
        }

        static queryDetailPageAddrList() {
            const detailMovieInfoArr = new Array();
            let elements = $('.video-number');
            for (var i = 0; i < elements.length; i++) {
                let element = elements[i];
                let text = element.innerText;
                let code = text.match(/[A-Za-z]{2,5}-\d{2,5}/)?.at(0);
                if (!code) continue;

                let movieInfo = Common.createMovieInfo();
                movieInfo.movieId = code;
                movieInfo.webURL = element.parentNode.href;
                detailMovieInfoArr.push(movieInfo);
            }

            if (detailMovieInfoArr.length <= 0) {
                return;
            }

            for (var j = 0; j < detailMovieInfoArr.length; j++) {

                var movieInfo = detailMovieInfoArr[j];
                if (movieInfo?.movieId) {
                    BottomPanel.addVideoAddrElementBottom("detail_addr", movieInfo);
                    getVideoURL_2(movieInfo).then((movieInfo) => {
                        if (!movieInfo.videoURL) return;

                        console.log(movieInfo.videoURL);
                        Common.updateVideoAddrElement("detail_addr", movieInfo);
                    });
                }
            }
        }

    }

    // *******************************************************************************************************8
    // TopPanel
    class TopPanel
    {

        static addTopButtonList() {
            // #fff-tbl
            var ppp2 = document.createElement("p");
            // ppp2.innerHTML = `
            //             <button onclick="loadMovieInfo()">加载</button>
            //             <button onclick="clearMovieAddrListBottom()">清除</button>
            //            `;

            /* 查询播放地址 */
            var btnLoad = document.createElement("button");
            btnLoad.style.marginRight = "10px";
            btnLoad.appendChild(document.createTextNode("加载"));
            btnLoad.addEventListener("click", function (){
                TopPanel.queryMoviePageAddrList();
            });
            ppp2.appendChild(btnLoad);

            /* 清除列表 */
            var btnClear = document.createElement("button");
            btnClear.style.marginLeft = "10px";
            btnClear.style.marginRight = "10px";
            btnClear.appendChild(document.createTextNode("清除"));
            btnClear.addEventListener("click", function (){
                TopPanel.clearMovieAddrListTop();
            });
            ppp2.appendChild(btnClear);

            /* 每行显示数 */
            var inputBoxNumber = document.createElement("input");
            inputBoxNumber.style.marginLeft = "50px";
            inputBoxNumber.style.marginRight = "5px";
            inputBoxNumber.style.width = "60px";
            inputBoxNumber.id = "page-row-number";
            inputBoxNumber.type = "number";
            inputBoxNumber.value = 2;
            ppp2.appendChild(inputBoxNumber);

            var btnBoxNumber = document.createElement("button");
            btnBoxNumber.style.marginLeft = "5px";
            btnBoxNumber.style.marginRight = "10px";
            btnBoxNumber.appendChild(document.createTextNode("每行显示数"));
            btnBoxNumber.addEventListener("click", function (){
                TopPanel.resetPageRowNumber();
            });
            ppp2.appendChild(btnBoxNumber);

            /* 列表中增加播放按钮 */
            var btnPlay = document.createElement("button");
            btnPlay.style.marginLeft = "10px";
            btnPlay.style.marginRight = "10px";
            btnPlay.appendChild(document.createTextNode("list播放"));
            btnPlay.addEventListener("click", function () {
                TopPanel.addPreplayButtonList();
            });
            ppp2.appendChild(btnPlay);

            /* 列表中增加播放按钮 */
            var btnPreVideo = document.createElement("button");
            btnPreVideo.style.marginLeft = "10px";
            btnPreVideo.style.marginRight = "10px";
            btnPreVideo.appendChild(document.createTextNode("list预告"));
            btnPreVideo.addEventListener("click", function () {
                PreVideoHome.createVideoCss();
                // initVideoList();
                // addMouseListener();

                PreVideoHome.initVideoListForItem();
            });
            ppp2.appendChild(btnPreVideo);

            /* 删除蒙版 */
            var btnDelPlayPanel = document.createElement("button");
            btnDelPlayPanel.style.marginLeft = "10px";
            btnDelPlayPanel.style.marginRight = "10px";
            btnDelPlayPanel.appendChild(document.createTextNode("删除蒙版"));
            btnDelPlayPanel.addEventListener("click", function (){
                TopPanel.delPlayPanel();
            });
            ppp2.appendChild(btnDelPlayPanel);

            /* 获取预览图地址 */
            var btnCopyTitle = document.createElement("button");
            btnCopyTitle.style.marginLeft = "10px";
            btnCopyTitle.style.marginRight = "10px";
            btnCopyTitle.appendChild(document.createTextNode("标题"));
            btnCopyTitle.addEventListener("click", async function () {
                try {
                    var classObj = TopPanel.copyAvClass(false);

                    var titleStrS = TopPanel.copyAvTitleS(false);
                    var nvYan = Common.parseYanYuan(classObj.yanYuan);
                    // navigator.clipboard.writeText(titleStrS + nvYan);
					Common.copyTextToClipboard(titleStrS + nvYan);
                    // Common.copyTextToClipboardForAaa(titleStrS + nvYan);

                    console.log('-------复制标题成功！！！！！！！！！');
                } catch (err) {
                    console.error('-------复制标题失败: ', err);
                }

            });
            ppp2.appendChild(btnCopyTitle);

            /* 获取预览图地址 */
            var btnCopyUrl = document.createElement("button");
            btnCopyUrl.style.marginLeft = "10px";
            btnCopyUrl.style.marginRight = "10px";
            btnCopyUrl.appendChild(document.createTextNode("弹窗"));
            btnCopyUrl.addEventListener("click", function () {
                // copyAvUrl();

                let movieInfo = Common.getMovieInfo();
                if (movieInfo.movieId) {
                    let avUrl = 'http://localhost:10168/premovie/index.html?avcode=' + movieInfo.movieId.toLowerCase();
                    Common.popupWindow(avUrl);
                }
            });
            ppp2.appendChild(btnCopyUrl);

            /* 获取预览图地址 */
            var btnCopyCover = document.createElement("button");
            btnCopyCover.style.marginLeft = "10px";
            btnCopyCover.style.marginRight = "10px";
            btnCopyCover.appendChild(document.createTextNode("封面"));
            btnCopyCover.addEventListener("click", async function () {
                try {
                    TopPanel.copyCoverImage();

                    console.log('-------复制图片成功！！！！！！！！！');
                } catch (err) {
                    console.error('-------复制图片失败: ', err);
                }

            });
            ppp2.appendChild(btnCopyCover);

            /* 获取预览图地址 */
            var btnCopyPreimage = document.createElement("button");
            btnCopyPreimage.style.marginLeft = "10px";
            btnCopyPreimage.style.marginRight = "10px";
            btnCopyPreimage.appendChild(document.createTextNode("预览图"));
            // btnCopyPreimage.addEventListener("click", copyPreimageAddr);
            btnCopyPreimage.addEventListener("click", function () {
                let movieInfo = Common.getMovieInfo();
                if (movieInfo.movieId) {
                    let imgUrl = "https://blogjav.net/?s=" + movieInfo.movieId + "&jump=auto";
                    // window.open(imgUrl, '_blank');
                    Common.popupWindow(imgUrl);
                }
            });
            ppp2.appendChild(btnCopyPreimage);

            /* 综合复制 */
            var btnPreview = document.createElement("button");
            btnPreview.style.marginLeft = "10px";
            btnPreview.style.marginRight = "10px";
            btnPreview.appendChild(document.createTextNode("预告片"));
            btnPreview.addEventListener("click", function () {
                TopPanel.copyPreviewAddr(true);
            });
            ppp2.appendChild(btnPreview);

            /* 综合复制 */
            var btnCopyAll2 = document.createElement("button");
            btnCopyAll2.style.marginLeft = "10px";
            btnCopyAll2.style.marginRight = "10px";
            btnCopyAll2.appendChild(document.createTextNode("综合复制2"));
            btnCopyAll2.addEventListener("click", async function () {
                try {
                    var classObj = TopPanel.copyAvClass(false)

                    var titleStrL = TopPanel.copyAvTitleL(false);
                    var avUrl = TopPanel.copyAvUrl(false);
                    var preimgAddr = TopPanel.copyPreimageAddr(false);
                    var prevideoAddr = TopPanel.copyPreviewAddr(false);
                    var zongHe = "【标  题】：" + titleStrL + "\r\n【系  列】：" + classObj.xilie + "\r\n【类  别】：" + classObj.leiBe + "\r\n【演  员】：" + classObj.yanYuan + "\r\n【页  面】：" + avUrl + "\r\n【预览图】：" + preimgAddr + "\r\n【预告片】：" + prevideoAddr + "\r\n";
                    navigator.clipboard.writeText(zongHe);

                    await Common.sleep(1000);

                    var titleStrS = TopPanel.copyAvTitleS(false);
                    var nvYan = Common.parseYanYuan(classObj.yanYuan);
                    navigator.clipboard.writeText(titleStrS + nvYan);

                    console.log('-------复制成功！！！！！！！！！');
                } catch (err) {
                    console.error('-------复制失败: ', err);
                }

            });
            ppp2.appendChild(btnCopyAll2);

            /* xxx */
            var btnMissAV = document.createElement("button");
            btnMissAV.style.marginLeft = "10px";
            btnMissAV.style.marginRight = "10px";
            btnMissAV.appendChild(document.createTextNode("MisAV"));
            // btnMissAV.addEventListener("click", copyPreimageAddr);
            btnMissAV.addEventListener("click", function () {
                let movieInfo = Common.getMovieInfo();
                if (movieInfo.movieId) {
                    let imgUrl = "https://missav2.one/cn/" + movieInfo.movieId.toLowerCase();
                    // window.open(imgUrl, '_blank');
                    Common.popupWindow(imgUrl);
                }
            });
            ppp2.appendChild(btnMissAV);

            /* 按钮面板 */
            var buttondiv = document.createElement("div");
            buttondiv.style.backgroundColor = "#3272dc";
            buttondiv.style.padding = "0.8rem";
            buttondiv.style.margin = 5;
            buttondiv.appendChild(ppp2);

            /* 地址面板 */
            var addrdiv = document.createElement("div");
            addrdiv.style.backgroundColor = "#eee";
            addrdiv.style.display = "block";
            addrdiv.id = "addr-list-top";

            /* 插入到页面中 */
            var element = document.getElementsByClassName("container")[0];
            if (element) {

                element.insertBefore(addrdiv, element.firstChild);
                element.insertBefore(buttondiv, addrdiv);
            }
        }

        static addPreplayButtonList() {

            var eleList = document.querySelectorAll(".box");
            for (let i = 0; i < eleList.length; i++) {
                var ele = eleList[i];

                const btnDev = ele.querySelector(".tags.has-addons");
                var avidStg = ele.querySelector(".video-title strong");

                if (!btnDev || !avidStg) {
                    continue;
                }

                var avid = avidStg.textContent;
                if (avid) {
                    var movieInfo = Common.createMovieInfo();
                    movieInfo.movieId = avid;

                    if (movieInfo?.movieId) {

                        getVideoURL_2(movieInfo).then((movieInfo) => {
                            if (!movieInfo.videoURL) return;

                            console.log(movieInfo.videoURL);

                            // var playBtn = document.createElement("a");
                            // playBtn.title = "播放";
                            // playBtn.style = "margin-right: 3px;background-color:#b2a333;";
                            // playBtn.textContent = "播放";
                            // playBtn.target = "_blank";
                            // playBtn.href = movieInfo.videoURL;

                            var playBtn = document.createElement("button");
                            playBtn.title = "播放";
                            playBtn.style = "margin-right: 3px;background-color:#b2a333;";
                            playBtn.textContent = "播放";
                            playBtn.addEventListener('click', function (evt) {
                                evt.preventDefault();
                                Common.copyTextToClipboardForAaa(movieInfo.videoURL);
                            })

                            const newCheckbox = document.createElement('input');
                            newCheckbox.type = 'checkbox'; // 设置类型为checkbox

                            if (btnDev) {
                                btnDev.appendChild(playBtn);
                                btnDev.appendChild(newCheckbox);
                            }
                        });
                    }
                }


            }
        }

        static clearMovieAddrListTop() {
            var ddd = document.getElementById("addr-list-top");
            ddd.innerHTML = "";
        }

        static queryMoviePageAddrList() {
            // document.getElementById("demo").innerHTML=Date();
            const movieInfoArr = new Array();
            //let elements = document.getElementsByClassName("sav-id infoFirst");
            let elements = $('.video-title strong');
            for (var i = 0; i < elements.length; i++) {
                let element = elements[i];
                let text = element.innerText;
                let code = text.match(/[A-Za-z]{2,5}-\d{2,5}/)?.at(0);
                if (!code) continue;

                let movieInfo = Common.createMovieInfo();
                movieInfo.movieId = code;
                movieInfo.webURL = element.parentNode.parentNode.href;
                movieInfoArr[i] = movieInfo;
                if (movieInfoArr[i]?.movieId) {
                    TopPanel.addVideoAddrElementTop("home_addr", movieInfo);
                    getVideoURL_2(movieInfoArr[i]).then((movieInfo) => {
                        if (!movieInfo.videoURL) return;

                        console.log(movieInfo.videoURL);
                        Common.updateVideoAddrElement("home_addr", movieInfo);
                    });
                }
            }
        }

        static addVideoAddrElementTop(pre_id, movieInfo) {
            var para = document.createElement("p");

            const newCheckbox = document.createElement('input');
            newCheckbox.type = 'checkbox'; // 设置类型为checkbox

            var addr = document.createElement("a");
            addr.id = pre_id + "_" + movieInfo.movieId;
            addr.href = movieInfo.videoURL;
            addr.target = "_blank";
            var text = document.createTextNode(movieInfo.videoURL);
            addr.append(text);

            var title = document.createElement("a");
            title.style.color = "#e5a555"
            title.href = movieInfo.webURL;
            title.target = "_blank";
            var text = document.createTextNode("***【" + movieInfo.movieId + "】***\t");
            title.append(text);

            para.appendChild(newCheckbox);
            para.appendChild(title)
            para.appendChild(addr);

            var element = document.getElementById("addr-list-top");
            if (element) {
                element.appendChild(para);
            }
        }


        static resetPageRowNumber() {
            var numberElement = document.getElementById("page-row-number");
            if (!numberElement) return;

            var element = document.getElementsByClassName("movie-list")[0];
            if (!element) return;

            var number = parseInt(numberElement.value);
            element.style.gridTemplateColumns = "repeat(" + number + ",minmax(0,1fr))";
        }

        static delPlayPanel() {
            // 查找具有类名'cover-container'的元素
            var img = document.querySelector('.column.column-video-cover img');
            var container = document.querySelector('.column.column-video-cover');
            if (container) {
                // 获取所有子元素
                var children = container.children;
                // 遍历子元素
                for (var i = children.length - 1; i >= 0; i--) { // 从后往前遍历以避免索引问题
                    if (children[i].classList.contains('cover-container')) {
                        // 如果包含，则移除该子元素
                        container.removeChild(children[i]);
                    }
                }

                container.appendChild(img);
            }

            // if (container) {
            //     // 删除该元素的class属性
            //     container.removeAttribute('class');
            //
            //     // 获取所有子元素
            //     var children = container.children;
            //
            //     // 遍历子元素
            //     for (var i = children.length - 1; i >= 0; i--) { // 从后往前遍历以避免索引问题
            //         // 检查子元素是否包含类名'play-button'
            //         if (children[i].classList.contains('play-button')) {
            //             // 如果包含，则移除该子元素
            //             container.removeChild(children[i]);
            //         }
            //     }
            // }
        }

        static copyPreimageAddr(isCopy = true) {
            var imgElement = document.getElementById('javRealImg');
            if (imgElement) {
                var src = imgElement.src;
                // 现在src变量包含了img元素的src属性值

                if (src && src.startsWith('https://')) {
                    // 第一步：替换"images"为"show"
                    src = src.replace('images', 'show');

                    // 第二步：移除第一个出现的"img"后跟两个数字
                    // 注意：这个正则表达式假设"img"后跟的恰好是两个数字，且这些数字是连续的
                    src = src.replace(/img\d{2}\./, '');

                    // 现在src变量已经按照要求修改过了
                    console.log("预览图页面地址：" + src); // 输出修改后的src值
                    //Common.copyTextToClipboard(src);
                    if (isCopy) {
                        navigator.clipboard.writeText(src);
                    }
                    return src;
                }
            }
        }

        static copyPreviewAddr(isCopy = true) {
            var videoElement = document.querySelector('#preview-video source');
            if (videoElement) {
                var src = videoElement.src;

                console.log("预告片地址：" + src); // 输出修改后的src值
                //Common.copyTextToClipboard(src);
                if (isCopy) {
                    navigator.clipboard.writeText(src);
                }
                return src;
            }
        }

        static copyAvTitleS(isCopy = true) {
            var titleStr = "";

            var titleEles = document.querySelectorAll('.title.is-4 strong');
            // titleEles.forEach(function (item){
            //     titleStr += item.textContent + "==";
            // });
            if (titleEles && titleEles.length >= 2) {
                titleStr += titleEles[0].textContent.trim();
                const selected = Common.getSelectedText();
                if (selected) {
                    titleStr += " " + selected.trim();
                } else {
                    titleStr += " " + Common.concatenateChineseCharacters_2(titleEles[1].textContent);
                }


                //Common.copyTextToClipboard(titleStr);
                if (isCopy) {
                    navigator.clipboard.writeText(titleStr);
                }
                return titleStr;
            }
        }

        static copyAvTitleL(isCopy = true) {
            var titleStr = "";

            var titleEles = document.querySelectorAll('.title.is-4 strong');
            if (titleEles && titleEles.length >= 2) {
                titleStr += titleEles[0].textContent;
                titleStr += " " + titleEles[1].textContent;

                //Common.copyTextToClipboard(titleStr);
                if (isCopy) {
                    navigator.clipboard.writeText(titleStr);
                }
                return titleStr;
            }
        }

        static copyAvClass(isCopy = true) {
            // 使用querySelectorAll获取所有匹配的strong标签
            const strongTags = document.querySelectorAll('.panel.movie-panel-info strong');
            let xilie = null;
            let leiBe = null;
            let yanYuan = null;

            // 使用for循环遍历这些strong标签
            for (let i = 0; i < strongTags.length; i++) {
                // 获取当前strong标签
                const strongTag = strongTags[i];
                const strongText = strongTag.textContent.trim();

                // 检查strong标签的文本内容是否等于"类别"（注意：处理可能的空格）
                if (strongText === '系列:') {
                    // 获取strong标签的同级元素
                    let destSpan = getSpanEle(strongTag);
                    if (destSpan) {
                        xilie = destSpan.innerText;
                    }
                }

                if (strongText === '類別:') {
                    let destSpan = getSpanEle(strongTag);
                    if (destSpan) {
                        leiBe = destSpan.innerText;
                    }
                }

                if (strongText === '演員:') {
                    let destSpan = getSpanEle(strongTag);
                    if (destSpan) {
                        yanYuan = destSpan.innerText;
                    }
                }
            }

        function getSpanEle(strongTag) {
                let sibling = strongTag.nextSibling;
                let destSpan = null

                // 遍历同级元素直到找到span或到达同级元素的末尾
                while (sibling) {
                    // 检查是否到达了同级span标签
                    if (sibling.nodeType === Node.ELEMENT_NODE && sibling.tagName === 'SPAN') {
                        // 如果找到了span标签，则执行相应的操作
                        destSpan = sibling;
                        break; // 找到后退出循环
                    }
                    // 否则，继续遍历下一个同级元素
                    sibling = sibling.nextSibling;
                }

                return destSpan;
            }

            return {xilie, leiBe, yanYuan};
        }

        static copyAvUrl(isCopy = true) {
            var currentURL = window.location.href;

            //Common.copyTextToClipboard(currentURL);
            if (isCopy) {
                navigator.clipboard.writeText(currentURL);
            }
            return currentURL;
        }

        static copyCoverImage() {
            var img = document.querySelector('.column-video-cover img');
            if (img) {
                img.crossOrigin = 'javdb367.com';
                // 创建一个新的Image对象，以便我们可以获取它的完整URL（包括跨域处理）
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');

                // 设置canvas的大小与图片相同
                canvas.width = img.naturalWidth;
                canvas.height = img.naturalHeight;

                // 将图片绘制到canvas上
                ctx.drawImage(img, 0, 0);

                // 将canvas转换为Blob对象
                canvas.toBlob(async (blob) => {
                    try {
                        // 创建一个File对象
                        const item = new ClipboardItem({[blob.type]: blob});
                        //const item = new ClipboardItem({ "image/png": blob });

                        // 写入剪贴板
                        await navigator.clipboard.write([item]);
                        console.log('图片已复制到剪贴板');
                    } catch (err) {
                        console.error('复制失败:', err);
                    }
                }, 'image/png'); // 注意：这里我们假设将图片转换为PNG格式
            }
        }

    }


    // ******************************************************************************************************************************************************
    class BigReimage {

        static addReimageButton() {


            /* 插入到页面中 */
            var element = document.getElementsByClassName("preview-images")[0];
            if (element) {
                var ppp2 = document.createElement("p");
                // ppp2.innerHTML = `
                //             <button onclick="loadMovieInfo()">加载</button>
                //             <button onclick="clearMovieAddrListBottom()">清除</button>
                //            `;

                /* 大预览图 */
                var btnPreImg = document.createElement("button");
                btnPreImg.style.marginLeft = "30px";
                btnPreImg.style.marginRight = "10px";
                btnPreImg.appendChild(document.createTextNode("大预览图"));
                btnPreImg.addEventListener("click", function (){
                    BigReimage.resetBigPreimg();
                });
                ppp2.appendChild(btnPreImg);

                /* 按钮面板 */
                var buttondiv = document.createElement("div");
                buttondiv.style.backgroundColor = "#eee";
                buttondiv.style.padding = "20px"
                buttondiv.appendChild(ppp2);

                element.parentNode.insertBefore(buttondiv, element);
            }
        }

        static resetBigPreimg() {
            var preImgConEle = document.getElementsByClassName("preview-images")[0];
            if (preImgConEle) {
                var style1 = document.createElement('style');
                style1.innerHTML = `
              .tile-images-2 {
                    display: grid;
                    grid-column-gap: 0.2rem;
                    -moz-column-gap: .2rem;
                    column-gap: 0.2rem;
                    grid-row-gap: 0.4rem;
                    row-gap: 0.4rem;
                    grid-template-columns: repeat(2,minmax(0,1fr));
                }
            `;
                document.body.appendChild(style1);

                preImgConEle.classList.remove("tile-images");
                preImgConEle.classList.add("tile-images-2");
            }

            var preImgEles = $(".preview-images img");
            if (preImgEles && preImgEles.length > 0) {
                // var style2 = document.createElement('style');
                // style2.innerHTML = `
                //   .img-2 {
                //         height: auto;
                //         max-width: 250%;
                //         width: 500px
                //     }
                // `;
                // document.body.appendChild(style2);

                for (var i = 0; i < preImgEles.length; i++) {
                    var preImgEle = preImgEles[i];
                    //console.log(preImgEle);

                    //preImgEle.classList.add("img-2");
                    preImgEle.src = preImgEle.parentNode.href;
                }
            }
        }

    }

    // ******************************************************************************************************************************************************
    // Cili
    class Cill {
        static addCiliButton() {
            var element = document.getElementsByClassName("magnet-links")[0];
            if (!element) {
                return;
            }

            var ppp2 = document.createElement("p");
            // ppp2.innerHTML = `
            //             <button onclick="loadMovieInfo()">加载</button>
            //             <button onclick="clearMovieAddrListBottom()">清除</button>
            //            `;

            /* 磁力搜索 */
            var btnCiliSreach = document.createElement("button");
            btnCiliSreach.style.marginLeft = "30px";
            btnCiliSreach.style.marginRight = "10px";
            btnCiliSreach.appendChild(document.createTextNode("磁力猫搜索"));
            btnCiliSreach.addEventListener("click", function (event) {
                var movieInfo = Common.getMovieInfo();
                if (movieInfo.movieId) {
                    var code = movieInfo.movieId;
                    // var newCode = code.toLowerCase().replace("-", "%20");
                    var newCode = code.toUpperCase();

                    var ciliUrl = "https://xiongmaoto.top/?searchstr=" + newCode;
                    // window.open(ciliUrl);
                    Common.popupWindow(ciliUrl, 900, 1100);
                }
            });
            ppp2.appendChild(btnCiliSreach);

            /* 磁力搜索 */
            var btnCiliSreach2 = document.createElement("button");
            btnCiliSreach2.style.marginLeft = "30px";
            btnCiliSreach2.style.marginRight = "10px";
            btnCiliSreach2.appendChild(document.createTextNode("磁力搜索2"));
            btnCiliSreach2.addEventListener("click", function (event) {
                var movieInfo = Common.getMovieInfo();
                if (movieInfo.movieId) {
                    var code = movieInfo.movieId;
                    // var newCode = code.toLowerCase().replace("-", "%20");
                    var newCode = code.toUpperCase();

                    var ciliUrl = "https://qjkkuqvc.2048173.xyz/search-" + newCode + "-0-1-1.html";
                    window.open(ciliUrl);
                }
            });
            ppp2.appendChild(btnCiliSreach2);

            /* 按钮面板 */
            var buttondiv = document.createElement("div");
            buttondiv.style.backgroundColor = "#eee";
            buttondiv.style.padding = "20px"
            buttondiv.appendChild(ppp2);

            /* 插入到页面中 */
            element.parentNode.insertBefore(buttondiv, element);

        }

        static ciliSreach() {
            var movieInfo = Common.getMovieInfo();
            if (movieInfo.movieId) {
                var code = movieInfo.movieId;
                var newCode = code.replace("-", " ");
                GM_setClipboard(newCode);
                //var ciliUrl = "https://xiongmaoto.top/search?keyword=" + newCode + "&sos=relevance&sofs=all&sot=all&soft=all&som=exact&p=1";
                var ciliUrl = "https://xiongmaoto.top";
                window.open(ciliUrl);
            }
        }
    }
    //





    // ******************************************************************************************************************************************************


})();
